package com.marketsim.simulator;

import com.marketsim.common.bean.OrderBean;
import com.marketsim.common.bean.PortfolioValueBean;
import com.marketsim.dao.ExchangesDAO;
import com.marketsim.dao.StockTickerDAO;

import java.math.BigDecimal;
import java.util.*;

/**
 * Created by Alex on 28.03.2014.
 */
public class MarketSimulator {

    private StockTickerDAO stockTickerDAO;

    private ExchangesDAO exchangesDAO;

    public List<PortfolioValueBean> simulate(List<OrderBean> orders, BigDecimal cashAmount) {
        List<PortfolioValueBean> result = new LinkedList<>();
        Date startDate = orders.get(0).getDate();
        Date endDate = orders.get(orders.size() - 1).getDate();
        if (startDate.after(endDate)) {
            throw new IllegalArgumentException();
        }

        Set<String> tickers = getUniqueTickers(orders);
        List<String> tickersList = new ArrayList<String>(tickers);
        PricesHistoricalData pricesHistory
                = stockTickerDAO.getStockInfo(tickersList, startDate, endDate);
        HashMap<String, Long> sharesPortfolio = new HashMap<>(tickersList.size());
        for (String ticker : tickersList) {
            sharesPortfolio.put(ticker, (long) 0);
        }

        ListIterator<OrderBean> ordersIterator = orders.listIterator();
        OrderBean currentOrder = ordersIterator.next();
        List<Date> tradingDates = getExchangesDAO().getNYSETradingDays(startDate, endDate);
        for (Date currentDate : tradingDates) {
            while (currentOrder != null && (currentOrder.getDate().equals(currentDate)
                    || currentOrder.getDate().before(currentDate))) {
                BigDecimal pricePerShare = pricesHistory.getPrice(currentOrder.getTicker(), currentOrder.getDate());
                switch (currentOrder.getOrderType()) {
                    case BUY_MARKET:
                        cashAmount = cashAmount.subtract(pricePerShare.multiply(new BigDecimal(currentOrder.getAmount())));
                        Long newSharesNumber1 = sharesPortfolio.get(currentOrder.getTicker()) + currentOrder.getAmount();
                        sharesPortfolio.put(currentOrder.getTicker(), newSharesNumber1);
                        break;

                    case SELL_MARKET:
                        cashAmount = cashAmount.add(pricePerShare.multiply(new BigDecimal(currentOrder.getAmount())));
                        Long newSharesNumber2 = sharesPortfolio.get(currentOrder.getTicker()) - currentOrder.getAmount();
                        sharesPortfolio.put(currentOrder.getTicker(), newSharesNumber2);
                        break;
                }
                System.out.println("Order executed " + currentOrder);
                if (ordersIterator.hasNext()) {
                    currentOrder = ordersIterator.next();
                } else {
                    currentOrder = null;
                }
            }

            PortfolioValueBean valueBean = new PortfolioValueBean();
            valueBean.setDate(currentDate);
            valueBean.setAmount(evaluatePortfolio(sharesPortfolio, cashAmount, pricesHistory, currentDate,
                    tickersList));
            result.add(valueBean);
        }

        return result;
    }

    private BigDecimal evaluatePortfolio(HashMap<String, Long> sharesPortfolio, BigDecimal cashAmount,
                                         PricesHistoricalData pricesHistory, Date currDate, List<String> tickersList) {
        BigDecimal result = new BigDecimal(0);
        result = result.add(cashAmount);
        for (String ticker : sharesPortfolio.keySet()) {
//            if (!sharesPortfolio.get(ticker).equals(0)) {
                BigDecimal pricePerShare = pricesHistory.getPrice(ticker, currDate);
                result = result.add(pricePerShare.multiply(new BigDecimal(sharesPortfolio.get(ticker))));
//            }
        }
        return result;
    }

    private Set<String> getUniqueTickers(List<OrderBean> orders) {
        Set<String> result = new HashSet<>();
        for (OrderBean order : orders) {
            result.add(order.getTicker());
        }
        return result;
    }

    public StockTickerDAO getStockTickerDAO() {
        return stockTickerDAO;
    }

    public void setStockTickerDAO(StockTickerDAO stockTickerDAO) {
        this.stockTickerDAO = stockTickerDAO;
    }

    public ExchangesDAO getExchangesDAO() {
        return exchangesDAO;
    }

    public void setExchangesDAO(ExchangesDAO exchangesDAO) {
        this.exchangesDAO = exchangesDAO;
    }
}
